/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file spwm_drv.c
 * @brief This file defines spwm driver functions
 */

/*******Includes***************************************************************/
#include "spwm_reg_access.h"
#include "clock_drv.h"


/*******Definitions************************************************************/

/*******Variables**************************************************************/
/*! @brief Table to save register base addresses for spwm instances */
static SPWM_Type* s_spwmRegBase[CONFIG_SPWM_INSTANCE_COUNT] = CONFIG_SPWM_REG_BASE_PTRS;

/*! @brief Table to save spwm module names */
static module_names_t s_spwmModuleName[CONFIG_SPWM_INSTANCE_COUNT] = CONFIG_SPWM_MODULE_NAMES;

/*! @brief Table to save ipwm interrupt status */
static spwm_int_state_t s_spwmIntStatus[CONFIG_SPWM_INSTANCE_COUNT] = {0U};

#if (CONFIG_SPWM_UNIFIED_IRQ_FEATURE == 1U)
/*! @brief Table to save spwm irq numbers */
static const IRQn_Type s_spwmIrqId[CONFIG_SPWM_INSTANCE_COUNT] = CONFIG_SPWM_IRQS;

/*! @brief Table to save spwm interrupt callbacks */
static spwm_callback_t s_spwmCallback[CONFIG_SPWM_INSTANCE_COUNT] = {NULL};
#else
/*! @brief Table to save spwm channel irq numbers */
static const IRQn_Type s_spwmChannelIrqId[CONFIG_SPWM_INSTANCE_COUNT] = CONFIG_SPWM_CHANNEL_IRQS;

/*! @brief Table to save spwm fault irq numbers */
static const IRQn_Type s_spwmFaultIrqId[CONFIG_SPWM_INSTANCE_COUNT] = CONFIG_SPWM_FAULT_IRQS;

/*! @brief Table to save spwm overflow irq numbers */
static const IRQn_Type s_spwmOverflowIrqId[CONFIG_SPWM_INSTANCE_COUNT] = CONFIG_SPWM_OVERFLOW_IRQS;

/*! @brief Table to save spwm interrupt callbacks */
static spwm_callback_t s_spwmChannelCallback[CONFIG_SPWM_INSTANCE_COUNT] = {NULL};

/*! @brief Table to save spwm interrupt callbacks */
static spwm_callback_t s_spwmFaultCallback[CONFIG_SPWM_INSTANCE_COUNT] = {NULL};

/*! @brief Table to save spwm interrupt callbacks */
static spwm_callback_t s_spwmOverflowCallback[CONFIG_SPWM_INSTANCE_COUNT] = {NULL};
#endif

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : SPWM_InitIndependentOutput
 *** Description   : Configure the SPWM channel independent output related functions
 ******************************************************************************/
void SPWM_InitIndependentOutput(uint32_t instance, const spwm_independent_output_config_t* const config)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    spwm_channel_t channel = SPWM_CH_0;
    uint8_t i = 0U;

    /* Enable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_spwmModuleName[instance], true);
    /* Set soft reset inactive of spwm module */
    CLOCK_SetModuleSoftResetState(s_spwmModuleName[instance], RESET_INACTIVE);

    ASSERT_PARAM(config->channelNum <= SPWM_CH_MAX);
    for (i = 0; i < config->channelNum; i++) {
        SPWM_REG_SetOutputInitLevel(regBase, config->independentChConfig[i].channel,
                                    config->independentChConfig[i].initLevel);
    }
    SPWM_REG_SetOutputInit(regBase, config->initOutputEn);

    for (i = 0; i < config->channelNum; i++) {
        ASSERT_PARAM(&(config->independentChConfig[i]) != NULL);

        channel = config->independentChConfig[i].channel;
        SPWM_REG_SetChannelValue(regBase, channel, config->independentChConfig[i].chValue);
        SPWM_REG_SetChPOL(regBase, channel, config->independentChConfig[i].polarity);
        SPWM_REG_SetChEventIntEn(regBase, channel, config->independentChConfig[i].chIntEn);
        SPWM_REG_SetChEventRatio(regBase, channel, config->independentChConfig[i].eventRatio);
        SPWM_REG_SetChEventEdgeLevel(regBase, channel, (uint8_t)config->independentChConfig[i].levelMode);
        SPWM_REG_SetChMatchTrigger(regBase, channel, config->independentChConfig[i].matchTriggerEn);
        SPWM_REG_SetChMode(regBase, channel, 3U);
    }

    SPWM_REG_SetCntMode(regBase, config->countMode);

    SPWM_REG_SetPrescaler(regBase, config->counterConfig.clkPsc);
    SPWM_REG_SetMod(regBase, config->counterConfig.modValue);
    SPWM_REG_SetCntInitVal(regBase, config->counterConfig.initValue);
    SPWM_REG_SetCounter(regBase, 0U);

    SPWM_REG_SetMinTrigger(regBase, config->minTriggerEn);
    SPWM_REG_SetMaxTrigger(regBase, config->maxTriggerEn);
    SPWM_REG_SetExternalTriggerRatio(regBase, config->triggerRatio);

#if (CONFIG_SPWM_UNIFIED_IRQ_FEATURE == 1U)
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmIrqId[instance]);
    }
    s_spwmCallback[instance] = config->callBack;
#else
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_EnableIRQ(s_spwmOverflowIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_DisableIRQ(s_spwmOverflowIrqId[instance]);
    }
    s_spwmChannelCallback[instance] = config->channelCallBack;
    s_spwmOverflowCallback[instance] = config->overflowCallBack;
#endif
    SPWM_REG_SetOverflowIntEn(regBase, config->counterConfig.overflowIntEn);
    SPWM_REG_SetUnderflowIntEn(regBase, config->counterConfig.underflowIntEn);
    SPWM_REG_SetOverflowEventRatio(regBase, config->counterConfig.cntOverflowFreq);

    SPWM_REG_SetCntEn(regBase, config->counterConfig.cntEn);
}

/*******************************************************************************
 *** Function Name : SPWM_InitCombineOutput
 *** Description   : Configure the SPWM channel combine output related functions
 ******************************************************************************/
void SPWM_InitCombineOutput(uint32_t instance, const spwm_combine_output_config_t* const config)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint8_t i = 0U;
    spwm_channel_t channel = SPWM_CH_0;
    spwm_dual_channel_t dualChannel = SPWM_DUAL_CH_0;

    /* Enable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_spwmModuleName[instance], true);
    /* Set soft reset inactive of spwm module */
    CLOCK_SetModuleSoftResetState(s_spwmModuleName[instance], RESET_INACTIVE);

    ASSERT_PARAM(config->channelNum <= SPWM_DUAL_CH_MAX);
    for (i = 0; i < config->channelNum; i++) {
        ASSERT_PARAM(&(config->combineChConfig[i]) != NULL);

        channel = config->combineChConfig[i].channel;
        SPWM_REG_SetOutputInitLevel(regBase, channel, config->combineChConfig[i].ch0InitLevel);
        SPWM_REG_SetOutputInitLevel(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                                    config->combineChConfig[i].ch1InitLevel);
    }
    SPWM_REG_SetOutputInit(regBase, config->initOutputEn);

    for (i = 0; i < config->channelNum; i++) {
        channel = config->combineChConfig[i].channel;
        dualChannel = (spwm_dual_channel_t)((uint8_t)(config->combineChConfig[i].channel) / 2U);

        SPWM_REG_SetDualChSymm(regBase, dualChannel, config->combineChConfig[i].symmetricalEn);

        SPWM_REG_SetChannelValue(regBase, channel, config->combineChConfig[i].cv0);
        SPWM_REG_SetChannelValue(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                                 config->combineChConfig[i].cv1);
        SPWM_REG_SetChPOL(regBase, channel, config->combineChConfig[i].ch0Polarity);
        SPWM_REG_SetChPOL(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                          config->combineChConfig[i].ch1Polarity);
        SPWM_REG_SetChMatchTrigger(regBase, channel, config->combineChConfig[i].ch0MatchTriggerEn);
        SPWM_REG_SetChMatchTrigger(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                                   config->combineChConfig[i].ch1MatchTriggerEn);
        SPWM_REG_SetChMatchDir(regBase, channel, config->combineChConfig[i].ch0MatchDir);
        SPWM_REG_SetChMatchDir(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                               config->combineChConfig[i].ch1MatchDir);
        SPWM_REG_SetChEventIntEn(regBase, channel, config->combineChConfig[i].ch0IntEn);
        SPWM_REG_SetChEventIntEn(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                                 config->combineChConfig[i].ch1IntEn);
        SPWM_REG_SetChEventRatio(regBase, channel, config->combineChConfig[i].ch0EventRatio);
        SPWM_REG_SetChEventRatio(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                                 config->combineChConfig[i].ch1EventRatio);

        SPWM_REG_SetChEventEdgeLevel(regBase, channel, (uint8_t)config->combineChConfig[i].levelMode);
        SPWM_REG_SetDualChCombine(regBase, dualChannel, true);
        SPWM_REG_SetChMode(regBase, channel, 2U);

        SPWM_REG_SetDualChComplement(regBase, dualChannel, config->combineChConfig[i].complementEn);
        SPWM_REG_SetDualChDeadtime(regBase, dualChannel, config->combineChConfig[i].deadtimeEn);
        SPWM_REG_SetDeadtimeVal(regBase, dualChannel, config->combineChConfig[i].deadtimeVal);
        SPWM_REG_SetDeadtimePsc(regBase, dualChannel, config->combineChConfig[i].deadtimePsc);
    }

    SPWM_REG_SetCntMode(regBase, config->countMode);

    SPWM_REG_SetPrescaler(regBase, config->counterConfig.clkPsc);
    SPWM_REG_SetMod(regBase, config->counterConfig.modValue);
    SPWM_REG_SetCntInitVal(regBase, config->counterConfig.initValue);
    SPWM_REG_SetCounter(regBase, 0U);

    SPWM_REG_SetMinTrigger(regBase, config->minTriggerEn);
    SPWM_REG_SetMaxTrigger(regBase, config->maxTriggerEn);
    SPWM_REG_SetExternalTriggerRatio(regBase, config->triggerRatio);

#if (CONFIG_SPWM_UNIFIED_IRQ_FEATURE == 1U)
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmIrqId[instance]);
    }
    s_spwmCallback[instance] = config->callBack;
#else
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_EnableIRQ(s_spwmOverflowIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_DisableIRQ(s_spwmOverflowIrqId[instance]);
    }
    s_spwmChannelCallback[instance] = config->channelCallBack;
    s_spwmOverflowCallback[instance] = config->overflowCallBack;
#endif
    SPWM_REG_SetOverflowIntEn(regBase, config->counterConfig.overflowIntEn);
    SPWM_REG_SetUnderflowIntEn(regBase, config->counterConfig.underflowIntEn);
    SPWM_REG_SetOverflowEventRatio(regBase, config->counterConfig.cntOverflowFreq);

    SPWM_REG_SetCntEn(regBase, config->counterConfig.cntEn);
}

/*******************************************************************************
 *** Function Name : SPWM_InitOutputCompare
 *** Description   : Configure the SPWM channel ouput compare related functions
 ******************************************************************************/
void SPWM_InitOutputCompare(uint32_t instance, const spwm_output_compare_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint8_t i = 0U;
    spwm_channel_t channel = SPWM_CH_0;

    /* Enable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_spwmModuleName[instance], true);
    /* Set soft reset inactive of spwm module */
    CLOCK_SetModuleSoftResetState(s_spwmModuleName[instance], RESET_INACTIVE);

    ASSERT_PARAM(config->channelNum <= SPWM_CH_MAX);
    for (i = 0; i < config->channelNum; i++) {
        ASSERT_PARAM(&(config->outputCompareChConfig[i]) != NULL);

        channel = config->outputCompareChConfig[i].channel;
        SPWM_REG_SetOutputInitLevel(regBase, channel, config->outputCompareChConfig[i].initLevel);
    }
    SPWM_REG_SetOutputInit(regBase, config->initOutputEn);

    for (i = 0; i < config->channelNum; i++) {
        channel = config->outputCompareChConfig[i].channel;
        SPWM_REG_SetChannelValue(regBase, channel, config->outputCompareChConfig[i].comparedValue);
        SPWM_REG_SetChEventEdgeLevel(regBase, channel, (uint8_t)config->outputCompareChConfig[i].mode);
        SPWM_REG_SetChMatchTrigger(regBase, channel, config->outputCompareChConfig[i].matchTriggerEn);
        SPWM_REG_SetChEventIntEn(regBase, channel, config->outputCompareChConfig[i].chIntEn);
        SPWM_REG_SetChEventRatio(regBase, channel, config->outputCompareChConfig[i].eventRatio);
        SPWM_REG_SetChMode(regBase, channel, 1U);
    }
    SPWM_REG_SetCntMode(regBase, SPWM_UP_COUNT);

    SPWM_REG_SetPrescaler(regBase, config->counterConfig.clkPsc);
    SPWM_REG_SetMod(regBase, config->counterConfig.modValue);
    SPWM_REG_SetCntInitVal(regBase, config->counterConfig.initValue);
    SPWM_REG_SetCounter(regBase, 0U);

    SPWM_REG_SetMinTrigger(regBase, config->minTriggerEn);
    SPWM_REG_SetMaxTrigger(regBase, config->maxTriggerEn);
    SPWM_REG_SetExternalTriggerRatio(regBase, config->triggerRatio);

#if (CONFIG_SPWM_UNIFIED_IRQ_FEATURE == 1U)
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmIrqId[instance]);
    }
    s_spwmCallback[instance] = config->callBack;
#else
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_EnableIRQ(s_spwmOverflowIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_DisableIRQ(s_spwmOverflowIrqId[instance]);
    }
    s_spwmChannelCallback[instance] = config->channelCallBack;
    s_spwmOverflowCallback[instance] = config->overflowCallBack;
#endif
    SPWM_REG_SetOverflowIntEn(regBase, config->counterConfig.overflowIntEn);
    SPWM_REG_SetOverflowEventRatio(regBase, config->counterConfig.cntOverflowFreq);

    SPWM_REG_SetCntEn(regBase, config->counterConfig.cntEn);
}

/*******************************************************************************
 *** Function Name : SPWM_InitSingleEdgeCapture
 *** Description   : Configure the SPWM channel signle edge capture related functions
 ******************************************************************************/
void SPWM_InitSingleEdgeCapture(uint32_t instance, const spwm_single_edge_capture_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint8_t i = 0U;
    spwm_channel_t channel = SPWM_CH_0;

    /* Enable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_spwmModuleName[instance], true);
    /* Set soft reset inactive of spwm module */
    CLOCK_SetModuleSoftResetState(s_spwmModuleName[instance], RESET_INACTIVE);

    ASSERT_PARAM(config->channelNum <= SPWM_CH_MAX);
    for (i = 0; i < config->channelNum; i++) {
        ASSERT_PARAM(&(config->channelConfig[i]) != NULL);

        channel = config->channelConfig[i].channel;
        SPWM_REG_SetChEventEdgeLevel(regBase, channel, (uint8_t)config->channelConfig[i].captureType);
        SPWM_REG_SetInputFilterVal(regBase, channel, config->channelConfig[i].filterValue);
        SPWM_REG_SetChEventIntEn(regBase, channel, config->channelConfig[i].chIntEn);
        SPWM_REG_SetChEventRatio(regBase, channel, config->channelConfig[i].eventRatio);
        SPWM_REG_SetChEventReset(regBase, channel, config->channelConfig[i].chRst);
        SPWM_REG_SetChMode(regBase, channel, 0U);
    }

    SPWM_REG_SetCntMode(regBase, SPWM_UP_COUNT);
    SPWM_REG_SetPrescaler(regBase, config->counterConfig.clkPsc);
    SPWM_REG_SetMod(regBase, config->counterConfig.modValue);
    SPWM_REG_SetCntInitVal(regBase, config->counterConfig.initValue);
    SPWM_REG_SetCounter(regBase, 0U);

    SPWM_REG_SetInputFilterPsc(regBase, config->filterPsc);

#if (CONFIG_SPWM_UNIFIED_IRQ_FEATURE == 1U)
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmIrqId[instance]);
    }
    s_spwmCallback[instance] = config->callBack;
#else
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_EnableIRQ(s_spwmOverflowIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_DisableIRQ(s_spwmOverflowIrqId[instance]);
    }
    s_spwmChannelCallback[instance] = config->channelCallBack;
    s_spwmOverflowCallback[instance] = config->overflowCallBack;
#endif
    SPWM_REG_SetOverflowIntEn(regBase, config->counterConfig.overflowIntEn);
    SPWM_REG_SetUnderflowIntEn(regBase, config->counterConfig.underflowIntEn);
    SPWM_REG_SetOverflowEventRatio(regBase, config->counterConfig.cntOverflowFreq);

    SPWM_REG_SetCntEn(regBase, config->counterConfig.cntEn);
}

/*******************************************************************************
 *** Function Name : SPWM_InitDualEdgeCapture
 *** Description   : Configure the SPWM channel dual edge capture related functions
 ******************************************************************************/
void SPWM_InitDualEdgeCapture(uint32_t instance, const spwm_dual_edge_capture_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint8_t i = 0U;
    spwm_channel_t channel = SPWM_CH_0;
    spwm_dual_channel_t dualChannel = SPWM_DUAL_CH_0;

    /* Enable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_spwmModuleName[instance], true);
    /* Set soft reset inactive of spwm module */
    CLOCK_SetModuleSoftResetState(s_spwmModuleName[instance], RESET_INACTIVE);

    ASSERT_PARAM(config->channelNum <= SPWM_DUAL_CH_MAX);
    for (i = 0; i < config->channelNum; i++) {
        ASSERT_PARAM(&(config->channelConfig[i]) != NULL);

        channel = config->channelConfig[i].channel;
        SPWM_REG_SetInputFilterVal(regBase, channel, config->channelConfig[i].filterValue);
        SPWM_REG_SetChEventIntEn(regBase, channel, config->channelConfig[i].ch0IntEn);
        SPWM_REG_SetChEventIntEn(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                                 config->channelConfig[i].ch1IntEn);
        SPWM_REG_SetChEventRatio(regBase, channel, config->channelConfig[i].ch0EventRatio);
        SPWM_REG_SetChEventRatio(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                                 config->channelConfig[i].ch1EventRatio);
        SPWM_REG_SetChEventReset(regBase, channel, config->channelConfig[i].ch0Rst);
        SPWM_REG_SetChEventReset(regBase, (spwm_channel_t)((uint8_t)channel + 1U),
                                 config->channelConfig[i].ch1Rst);

        dualChannel = (spwm_dual_channel_t)((uint8_t)(config->channelConfig[i].channel) / 2U);
        SPWM_REG_SetDualEdgeCapture(regBase, dualChannel, true);
        SPWM_REG_SetDualChDecap(regBase, dualChannel, true);

        if (config->channelConfig[i].continousEn == true) {
            SPWM_REG_SetChMode(regBase, channel, 1U);
        } else {
            SPWM_REG_SetChMode(regBase, channel, 0U);
        }

        switch (config->channelConfig[i].captureType) {
            case SPWM_POSITIVE_PLUSE_WIDTH_CAPTURE:
                SPWM_REG_SetChEventEdgeLevel(regBase, channel, (uint8_t)SPWM_RISING_EDGE_DETECT);
                SPWM_REG_SetChEventEdgeLevel(regBase, (spwm_channel_t)((uint8_t)channel + 1U), (uint8_t)SPWM_FALLING_EDGE_DETECT);
                break;

            case SPWM_NEGATIVE_PLUSE_WIDTH_CAPTURE:
                SPWM_REG_SetChEventEdgeLevel(regBase, channel, (uint8_t)SPWM_FALLING_EDGE_DETECT);
                SPWM_REG_SetChEventEdgeLevel(regBase, (spwm_channel_t)((uint8_t)channel + 1U), (uint8_t)SPWM_RISING_EDGE_DETECT);
                break;

            case SPWM_RISING_EDGE_PERIOD_CAPTURE:
                SPWM_REG_SetChEventEdgeLevel(regBase, channel, (uint8_t)SPWM_RISING_EDGE_DETECT);
                SPWM_REG_SetChEventEdgeLevel(regBase, (spwm_channel_t)((uint8_t)channel + 1U), (uint8_t)SPWM_RISING_EDGE_DETECT);
                break;

            case SPWM_FALLING_EDGE_PERIOD_CAPTURE:
                SPWM_REG_SetChEventEdgeLevel(regBase, channel, (uint8_t)SPWM_FALLING_EDGE_DETECT);
                SPWM_REG_SetChEventEdgeLevel(regBase, (spwm_channel_t)((uint8_t)channel + 1U), (uint8_t)SPWM_FALLING_EDGE_DETECT);
                break;

            default:
                break;
        }
    }

    SPWM_REG_SetHallMode(regBase, config->hallEn);

    SPWM_REG_SetCntMode(regBase, SPWM_UP_COUNT);
    SPWM_REG_SetPrescaler(regBase, config->counterConfig.clkPsc);
    SPWM_REG_SetMod(regBase, config->counterConfig.modValue);
    SPWM_REG_SetCntInitVal(regBase, config->counterConfig.initValue);
    SPWM_REG_SetCounter(regBase, 0U);

    SPWM_REG_SetInputFilterPsc(regBase, config->filterPsc);

#if (CONFIG_SPWM_UNIFIED_IRQ_FEATURE == 1U)
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmIrqId[instance]);
    }
    s_spwmCallback[instance] = config->callBack;
#else
    if (config->intEn == true) {
        NVIC_EnableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_EnableIRQ(s_spwmOverflowIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmChannelIrqId[instance]);
        NVIC_DisableIRQ(s_spwmOverflowIrqId[instance]);
    }
    s_spwmChannelCallback[instance] = config->channelCallBack;
    s_spwmOverflowCallback[instance] = config->overflowCallBack;
#endif

    SPWM_REG_SetOverflowIntEn(regBase, config->counterConfig.overflowIntEn);
    SPWM_REG_SetUnderflowIntEn(regBase, config->counterConfig.underflowIntEn);
    SPWM_REG_SetOverflowEventRatio(regBase, config->counterConfig.cntOverflowFreq);

    SPWM_REG_SetCntEn(regBase, config->counterConfig.cntEn);
}

/*******************************************************************************
 *** Function Name : SPWM_SetDualChannelInvert
 *** Description   : Configure the SPWM dual channel invert related functions
 ******************************************************************************/
void SPWM_SetDualChannelInvert(uint32_t instance, spwm_dual_channel_t dualChannel, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(dualChannel < SPWM_DUAL_CH_MAX);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetDualChInvert(regBase, dualChannel, enable);
}

/*******************************************************************************
 *** Function Name : SPWM_SetChannelSoftwareControl
 *** Description   : Configure the SPWM channel software output control related functions
 ******************************************************************************/
void SPWM_SetChannelSoftwareControl(uint32_t instance, spwm_channel_t channel, bool enable,
                                    spwm_output_level_t level)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < SPWM_CH_MAX);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetSoftwareControlLevel(regBase, channel, level);
    SPWM_REG_SetSoftwareControlEn(regBase, channel, enable);
}

/*******************************************************************************
 *** Function Name : SPWM_SetChannelOutputMask
 *** Description   : Configure the SPWM channel output mask related functions
 ******************************************************************************/
void SPWM_SetChannelOutputMask(uint32_t instance, spwm_channel_t channel, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < SPWM_CH_MAX);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetOutputMask(regBase, channel, enable);
}

/*******************************************************************************
 *** Function Name : SPWM_InitFaultControl
 *** Description   : Configure the SPWM fault control related functions
 ******************************************************************************/
void SPWM_InitFaultControl(uint32_t instance, const spwm_fault_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint8_t i = 0U;
    spwm_fault_input_t faultChannel = SPWM_FAULT_INPUT_0;

    for (i = 0U; i < config->faultChannelNum; i++) {
        faultChannel = config->channelConfig[i].faultChannel;
        SPWM_REG_SetFaultInput(regBase, faultChannel, config->channelConfig[i].faultInputEn);
        SPWM_REG_SetFaultInputFilterEn(regBase, faultChannel, config->channelConfig[i].faultFilterEn);
        SPWM_REG_SetFaultPolarity(regBase, faultChannel, config->channelConfig[i].faultPolarity);
    }
    for (i = 0U; i < (uint8_t)SPWM_DUAL_CH_MAX; i++) {
        SPWM_REG_SetDualChFault(regBase, (spwm_dual_channel_t)((uint8_t)SPWM_DUAL_CH_0 + i), config->faultCtrlOutputEn[i]);
    }
    SPWM_REG_SetInputFilterPsc(regBase, config->filterPsc);
    SPWM_REG_SetFaultInputFilterVal(regBase, config->filterValue);
    SPWM_REG_SetFaultHizOutput(regBase, config->HizEn);
    SPWM_REG_SetFaultMode(regBase, config->mode);
    SPWM_REG_ClrFAULTF(regBase);
    SPWM_REG_SetFaultEnable(regBase, config->faultEn);
#if (CONFIG_SPWM_INDEPENDENT_FAULT_INT == 1U)
    if (config->faultIntEn == true) {
        NVIC_EnableIRQ(s_spwmFaultIrqId[instance]);
    } else {
        NVIC_DisableIRQ(s_spwmFaultIrqId[instance]);
    }
    s_spwmFaultCallback[instance] = config->faultCallBack;
#endif
    SPWM_REG_SetFaultIntEnable(regBase, config->faultIntEn);
}

/*******************************************************************************
 *** Function Name : SPWM_SetModDither
 *** Description   : Configure the SPWM modulo dither related functions
 ******************************************************************************/
void SPWM_SetModDither(uint32_t instance, uint8_t modDitherValue)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetMODDither(regBase, modDitherValue);
}

/*******************************************************************************
 *** Function Name : SPWM_SetChDither
 *** Description   : Configure the SPWM channel dither related functions
 ******************************************************************************/
void SPWM_SetChDither(uint32_t instance, spwm_channel_t channel, uint8_t chDitherValue)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < SPWM_CH_MAX);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetChDither(regBase, channel, chDitherValue);
}

/*******************************************************************************
 *** Function Name : SPWM_SetCntInitVal
 *** Description   : Set the SPWM counter initial value
 ******************************************************************************/
void SPWM_SetCntInitVal(uint32_t instance, uint16_t cntInitValue)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetCntInitVal(regBase, cntInitValue);
}

/*******************************************************************************
 *** Function Name : SPWM_SetChannelValue
 *** Description   : Set the SPWM channel value
 ******************************************************************************/
void SPWM_SetChannelValue(uint32_t instance, spwm_channel_t channel, uint16_t channelValue)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < SPWM_CH_MAX);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetChannelValue(regBase, channel, channelValue);
}

/*******************************************************************************
 *** Function Name : SPWM_SetMod
 *** Description   : Set the SPWM counter modulo value
 ******************************************************************************/
void SPWM_SetMod(uint32_t instance, uint16_t modValue)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetMod(regBase, modValue);
}

/*******************************************************************************
 *** Function Name : SPWM_SetCounter
 *** Description   : Set the SPWM counter value, this function is used to make
 ***                 the counter restart from cntin, cntValue has no valid meaning
 ******************************************************************************/
void SPWM_SetCounter(uint32_t instance, uint16_t cntValue)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetCounter(regBase, cntValue);
}

/*******************************************************************************
 *** Function Name : SPWM_GetCounter
 *** Description   : Get the SPWM current counter value
 ******************************************************************************/
uint16_t SPWM_GetCounter(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint16_t cntValue = 0U;

    cntValue = SPWM_REG_GetCounter(regBase);

    return cntValue;
}

/*******************************************************************************
 *** Function Name : SPWM_GetChannelValue
 *** Description   : Get SPWM channel value
 ******************************************************************************/
uint16_t SPWM_GetChannelValue(uint32_t instance, spwm_channel_t channel)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);
    ASSERT_PARAM(channel < SPWM_CH_MAX);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint16_t channelValue = 0U;

    channelValue = SPWM_REG_GetChannelValue(regBase, channel);

    return channelValue;
}

/*******************************************************************************
 *** Function Name : SPWM_GetChannelLevel
 *** Description   : Get the SPWM channel level
 ******************************************************************************/
uint8_t SPWM_GetChannelLevel(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint8_t level = 0U;

    level = SPWM_REG_GetChLevel(regBase);

    return level;
}

/*******************************************************************************
 *** Function Name : SPWM_GetFaultStatus
 *** Description   : Get SPWM all fault flag
 ******************************************************************************/
uint8_t SPWM_GetFaultStatus(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint32_t status = 0U;

    status = SPWM_REG_GetFaultStatus(regBase);

    return (uint8_t)status;
}

/*******************************************************************************
 *** Function Name : SPWM_SetWriteProtect
 *** Description   : Enable/Disable the SPWM write protect
 ******************************************************************************/
void SPWM_SetWriteProtect(uint32_t instance, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetWriteProtect(regBase, enable);
}

/*******************************************************************************
 *** Function Name : SPWM_InitSyncContol
 *** Description   : Configure the SPWM register sync related functions
 ******************************************************************************/
void SPWM_InitSyncContol(uint32_t instance, spwm_sync_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];
    uint8_t i = 0U;

    /* Set sync trigger mode */
    if (config->syncTriggerMode == SPWM_SYNC_TRIGGER_SOFTWARE) {
        SPWM_REG_SetModSyncTriggerMode(regBase, SPWM_SYNC_TRIGGER_SOFTWARE, config->counterSyncEn);
        SPWM_REG_SetOutputMaskSyncTriggerMode(regBase, SPWM_SYNC_TRIGGER_SOFTWARE, true);
        SPWM_REG_SetInvertSyncTriggerMode(regBase, SPWM_SYNC_TRIGGER_SOFTWARE, true);
        SPWM_REG_SetSWOCTRLSyncTriggerMode(regBase, SPWM_SYNC_TRIGGER_SOFTWARE, true);
    } else { /* hardware trigger mode*/
        SPWM_REG_SetModSyncTriggerMode(regBase, SPWM_SYNC_TRIGGER_HARDWARE, config->counterSyncEn);
        SPWM_REG_SetOutputMaskSyncTriggerMode(regBase, SPWM_SYNC_TRIGGER_HARDWARE, true);
        SPWM_REG_SetInvertSyncTriggerMode(regBase, SPWM_SYNC_TRIGGER_HARDWARE, true);
        SPWM_REG_SetSWOCTRLSyncTriggerMode(regBase, SPWM_SYNC_TRIGGER_HARDWARE, true);
    }

    SPWM_REG_SetOutputMaskSyncEn(regBase, config->outmaskSyncEn);
    SPWM_REG_SetSWOCTRLSyncMode(regBase, config->outCtrlSyncEn);
    SPWM_REG_SetInvertSyncMode(regBase, config->inverterSyncEn);

    SPWM_REG_SetHardwareTrig0En(regBase, config->hardwareSync0En);
    SPWM_REG_SetHardwareTrig1En(regBase, config->hardwareSync1En);

    SPWM_REG_SetHwTriggerSyncMode(regBase, config->autoClearHWTriggerEn);
    if (config->maxLoadingPointEn || config->minLoadingPointEn) {
        SPWM_REG_SetMaxLoadPoint(regBase, config->maxLoadingPointEn);
        SPWM_REG_SetMinLoadPoint(regBase, config->minLoadingPointEn);
        SPWM_REG_SetCounterSyncTriggerMode(regBase, config->syncTriggerMode, false);
    } else {
        SPWM_REG_SetCounterSyncTriggerMode(regBase, config->syncTriggerMode, true);
    }

    for (i = 0U; i < (uint8_t)SPWM_DUAL_CH_MAX; i++) {
        SPWM_REG_SetCnvSync(regBase, (spwm_dual_channel_t)((uint8_t)SPWM_DUAL_CH_0 + i), config->counterSyncEn);
    }

    SPWM_REG_SetSyncEnable(regBase, config->syncEn);
}

/*******************************************************************************
 *** Function Name : SPWM_SetSoftwareTriggerSync
 *** Description   : Set software trigger to sync register
 ******************************************************************************/
void SPWM_SetSoftwareTriggerSync(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetSoftwareTriggerSync(regBase, true);
}

/*******************************************************************************
 *** Function Name : SPWM_SetCntEnable
 *** Description   : Set the SPWM counter enable
 ******************************************************************************/
void SPWM_SetCntEnable(uint32_t instance, bool cntEn)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetCntEn(regBase, cntEn);
}

/*******************************************************************************
 *** Function Name : SPWM_SetGlobalTimeBaseEnable
 *** Description   : Enable/Disable the SPWM global time base
 ******************************************************************************/
void SPWM_SetGlobalTimeBaseEnable(uint32_t instance, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetGlobalTimeBaseEnable(regBase, enable);
}

/*******************************************************************************
 *** Function Name : SPWM_SetGlobalTimeBaseOutput
 *** Description   : Enable/Disable the SPWM global time base output
 ******************************************************************************/
void SPWM_SetGlobalTimeBaseOutput(uint32_t instance, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    SPWM_Type* regBase = s_spwmRegBase[instance];

    SPWM_REG_SetGlobalTimeBaseOutput(regBase, enable);
}

/*******************************************************************************
 *** Function Name : SPWM_Deinit
 *** Description   : Deinit SPWM
 ******************************************************************************/
void SPWM_Deinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_SPWM_INSTANCE_COUNT);

    /* Set soft reset active of spwm module */
    CLOCK_SetModuleSoftResetState(s_spwmModuleName[instance], RESET_ACTIVE);
    /* Disable spwm module clock */
    CLOCK_SetModuleBusClockEnable(s_spwmModuleName[instance], false);

#if (CONFIG_SPWM_UNIFIED_IRQ_FEATURE == 1U)
    NVIC_DisableIRQ(s_spwmIrqId[instance]);
    NVIC_ClearPendingIRQ(s_spwmIrqId[instance]);
    s_spwmCallback[instance] = NULL;
#else
    NVIC_DisableIRQ(s_spwmChannelIrqId[instance]);
    NVIC_ClearPendingIRQ(s_spwmChannelIrqId[instance]);
    s_spwmChannelCallback[instance] = NULL;

    NVIC_DisableIRQ(s_spwmFaultIrqId[instance]);
    NVIC_ClearPendingIRQ(s_spwmFaultIrqId[instance]);
    s_spwmFaultCallback[instance] = NULL;

    NVIC_DisableIRQ(s_spwmOverflowIrqId[instance]);
    NVIC_ClearPendingIRQ(s_spwmOverflowIrqId[instance]);
    s_spwmOverflowCallback[instance] = NULL;
#endif
}

#if (CONFIG_SPWM_UNIFIED_IRQ_FEATURE == 1U)
/*******************************************************************************
 *** Function Name : SPWM_IRQHandlerProcess
 *** Description   : SPWM interrupt interrupt implement
 ******************************************************************************/
void SPWM_IRQHandlerProcess(uint32_t instance)
{
    SPWM_Type* regBase = s_spwmRegBase[instance];
    spwm_int_state_t* intStatus = &s_spwmIntStatus[instance];

    intStatus->overflowStatus = SPWM_REG_GetOverflowFlag(regBase);
    intStatus->underflowStatus = SPWM_REG_GetUnderflowFlag(regBase);
    intStatus->channelStatus = SPWM_REG_GetChStatus(regBase);
    intStatus->faultStatus = SPWM_REG_GetFaultStatus(regBase);

    if (intStatus->overflowStatus) {
        SPWM_REG_ClrOverflowFlag(regBase);
    }

    if (intStatus->underflowStatus) {
        SPWM_REG_ClrUnderflowFlag(regBase);
    }

    if (intStatus->channelStatus) {
        SPWM_REG_ClrChStatus(regBase, intStatus->channelStatus);
    }

    if (intStatus->faultStatus) {
        SPWM_REG_ClrFaultStatus(regBase, intStatus->faultStatus);
        //It is up to the user to decide how to deal with it
    }

    if ((s_spwmCallback[instance] != NULL) && \
            ((intStatus->overflowStatus > 0U) || \
             (intStatus->underflowStatus > 0U) || \
             (intStatus->channelStatus > 0U) || \
             (intStatus->faultStatus > 0U))) {
        s_spwmCallback[instance](instance, (void*)intStatus);
    }
}
#else
/*******************************************************************************
 *** Function Name : SPWM_Channel_IRQHandlerProcess
 *** Description   : SPWM channel interrupt interrupt implement
 ******************************************************************************/
void SPWM_Channel_IRQHandlerProcess(uint32_t instance)
{
    SPWM_Type* regBase = s_spwmRegBase[instance];
    spwm_int_state_t* intStatus = &s_spwmIntStatus[instance];

    intStatus->channelStatus = SPWM_REG_GetChStatus(regBase);
    if (intStatus->channelStatus) {
        SPWM_REG_ClrChStatus(regBase, intStatus->channelStatus);
    }

    if ((s_spwmChannelCallback[instance] != NULL) && (intStatus->channelStatus > 0U)) {
        s_spwmChannelCallback[instance](instance, (void*)intStatus);
    }
}

/*******************************************************************************
 *** Function Name : SPWM_Fault_IRQHandlerProcess
 *** Description   : SPWM fault interrupt interrupt implement
 ******************************************************************************/
void SPWM_Fault_IRQHandlerProcess(uint32_t instance)
{
    SPWM_Type* regBase = s_spwmRegBase[instance];
    spwm_int_state_t* intStatus = &s_spwmIntStatus[instance];

    intStatus->faultStatus = SPWM_REG_GetFaultStatus(regBase);
    if ((s_spwmFaultCallback[instance] != NULL) && (intStatus->faultStatus > 0U)) {
        s_spwmFaultCallback[instance](instance, (void*)intStatus);
    }

    if (intStatus->faultStatus) {
        SPWM_REG_ClrFaultStatus(regBase, intStatus->faultStatus);
        //It is up to the user to decide how to deal with it
    }
}

/*******************************************************************************
 *** Function Name : SPWM_Overflow_IRQHandlerProcess
 *** Description   : SPWM overflow interrupt interrupt implement
 ******************************************************************************/
void SPWM_Overflow_IRQHandlerProcess(uint32_t instance)
{
    SPWM_Type* regBase = s_spwmRegBase[instance];
    spwm_int_state_t* intStatus = &s_spwmIntStatus[instance];

    intStatus->overflowStatus = SPWM_REG_GetOverflowFlag(regBase);
    intStatus->underflowStatus = SPWM_REG_GetUnderflowFlag(regBase);

    if (intStatus->overflowStatus) {
        SPWM_REG_ClrOverflowFlag(regBase);
    }

    if (intStatus->underflowStatus) {
        SPWM_REG_ClrUnderflowFlag(regBase);
    }

    if ((s_spwmOverflowCallback[instance] != NULL) && \
            ((intStatus->overflowStatus > 0U) || \
             (intStatus->underflowStatus > 0U))) {
        s_spwmOverflowCallback[instance](instance, (void*)intStatus);
    }
}
#endif
/*******EOF********************************************************************/

